package wailsutil

import (
	"context"
	"encoding/json"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/jadefox10200/goprint"
	"github.com/wailsapp/wails/v2/pkg/runtime"
	"gopkg.in/ini.v1"
)

var (
	appFun             *AppFun
	iniFilePath        string
	windowInfoFilePath string
	messageMonitorIp   []byte //接收信息的服务端IP
	messageMonitorPort int    //接收信息的服务端端口
)

// App struct
type AppFun struct {
	SysType        string //系统类型
	IsShow         bool   //窗体当前是否显示
	UseBeforeClose bool   //是否显示关闭前提示
	IsOnTop        bool   //窗口是否置顶
	Ctx            context.Context
	Width          int
	Height         int
	Top            int
	Left           int
	IsMaxWindow    bool
	isDomReady     bool   //前端加载完毕
	ClearTrayFun   func() //清理托盘函数
	appConfigFile  string //系统配置文件
}

// NewApp creates a new App application struct
func NewApp(iniFilePath, sysType string) *AppFun {
	result := &AppFun{UseBeforeClose: true, IsShow: true}
	result.SysType = sysType
	result.appConfigFile = iniFilePath
	result.createInitFile(iniFilePath) //创建配置文件
	result.readInit()                  //读取配置文件

	return result
}

// 创建配置文件
func (a *AppFun) createInitFile(Path string) {
	// 获取文件的完整路径
	var err error
	iniFilePath, err = filepath.Abs(Path)
	if err != nil {
		clogger.Error("获取绝对路径失败:" + err.Error())
	}

	_, err = os.Stat(iniFilePath) // 尝试获取文件信息
	if err == nil {               // 如果文件存在并且没有错误发生，则说明存在配置文件
		return
	}

	// 检查文件夹是否存在，如果不存在则创建
	dir := filepath.Dir(iniFilePath)
	err = os.MkdirAll(dir, 0755)
	if err != nil {
		clogger.Error("创建目录失败:" + err.Error())
	}

	// 打开文件，如果文件不存在，os.OpenFile会创建它
	file, err := os.OpenFile(iniFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		clogger.Error("打开或创建文件失败:" + err.Error())
		return
	}

	file.WriteString("")
	file.Close()

	cfg, err := ini.Load(iniFilePath)
	if err != nil {
		clogger.Error("读取配置文件异常:" + err.Error())
		return
	}

	cfg.Section("System").Key("Name").SetValue("GNotepad")
	cfg.Section("System").Key("Title").SetValue("笔记")
	cfg.Section("System").Key("OnlyRun").SetValue("1")

	cfg.SaveTo(iniFilePath)
}

/**
 * 读取配置文件
 */
func (a *AppFun) readInit() {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		clogger.Error("取程序路径异常:" + err.Error())
	}

	// 检查文件夹是否存在，如果不存在则创建
	err = os.MkdirAll(dir+"/temp", 0755)
	if err != nil {
		clogger.Error("创建目录失败:" + err.Error())
	}

	//判断是否需要进行开启发送信息服务
	cfg, err := ini.Load(a.appConfigFile)
	if err == nil {
		temp := cfg.Section("MessageMonitor").Key("SeverIp") //接收信息的服务端IP
		if temp != nil {
			array := strings.Split(temp.String(), ".")
			if len(array) == 4 {
				ip := make([]byte, 4)
				for i := 0; i < len(array); i++ {
					ipPart, err := strconv.Atoi(array[i]) // 将字符串转换为整数
					if err != nil || ipPart < 0 || ipPart > 255 {
						ip = nil
						break
					}
					ip[i] = byte(ipPart) // 转换为字节
				}

				if ip != nil {
					messageMonitorIp = ip
				}
			}
		}

		temp = cfg.Section("MessageMonitor").Key("SeverPort") //接收信息的服务端端口
		if temp != nil {
			messageMonitorPort, err = temp.Int()
			if err != nil {
				messageMonitorPort = -1
			}
		}
	}

	windowInfoFilePath = strings.Replace(dir, "\\", "/", -1) + "/temp/windowInfo.ini"
	_, err = os.Stat(windowInfoFilePath) // 如果文件不存在则创建
	if err != nil {
		if os.IsNotExist(err) {
			os.Create(windowInfoFilePath)
		}
	}

	cfg, err = ini.Load(windowInfoFilePath)
	if err != nil {
		clogger.Error("读取配置文件异常:" + err.Error())
		a.Width = 960
		a.Height = 600
		a.Top = -1024
		a.Left = -1024
		a.IsMaxWindow = false
		return
	}

	section := cfg.Section("Window")
	a.Width, err = section.Key("iWidth").Int()
	if err != nil {
		a.Width = 960
	}

	a.Height, err = section.Key("iHeight").Int()
	if err != nil {
		a.Height = 600
	}

	a.Top, err = section.Key("iTop").Int()
	if err != nil {
		a.Top = -1024
	}

	a.Left, err = section.Key("iLeft").Int()
	if err != nil {
		a.Left = -1024
	}

	temp, _ := section.Key("isMaxWindow").Int()
	a.IsMaxWindow = temp == 1
}

// 一旦 Wails 分配了它需要的资源，就会调用 startup 方法，它是创建资源、设置事件侦听器以及应用程序在启动时需要的任何其他东西的好地方
func (a *AppFun) Startup(ctx context.Context) {
	a.Ctx = ctx

	if (a.Top <= -1024) || (a.Left <= -1024) {
		runtime.WindowCenter(a.Ctx) //居中
		return
	}

	runtime.WindowSetPosition(a.Ctx, a.Left, a.Top) //指定位置
}

// 按配置设置位置
func (a *AppFun) SetPositionByIni() {
	if (a.Top <= (-a.Height)) || (a.Left <= (-a.Width)) {
		runtime.WindowCenter(a.Ctx) //居中
		return
	}

	runtime.WindowSetPosition(a.Ctx, a.Left, a.Top)
}

// 在前端加载完毕 index.html 及其资源后调用此回调。 它提供了应用程序上下文
func (a *AppFun) DomReady(ctx context.Context) {
	a.isDomReady = true
	//runtime.WindowShow(a.ctx)
}

// 在关闭过程结束时由 Wails 调用。 这是释放内存和执行关闭任务的好地方
func (a *AppFun) Shutdown(ctx context.Context) {
	// SaveWindowInfo(ctx)//无法获得window信息,不能在这里调用
}

func SaveWindowInfo(ctx context.Context) {
	cfg, err := ini.Load(windowInfoFilePath)
	if err != nil {
		clogger.Error("读取配置文件异常:" + err.Error())
		return
	}

	if runtime.WindowIsMaximised(ctx) { //窗口最大化情况下不能保存窗口位置和大小,否则会都是最大化
		cfg.Section("Window").Key("isMaxWindow").SetValue("1")
		cfg.SaveTo(windowInfoFilePath)
		return
	}

	iWidth, iHeight := runtime.WindowGetSize(ctx)
	iLeft, iTop := runtime.WindowGetPosition(ctx)

	cfg.Section("Window").Key("iWidth").SetValue(strconv.Itoa(iWidth))
	cfg.Section("Window").Key("iHeight").SetValue(strconv.Itoa(iHeight))
	cfg.Section("Window").Key("iLeft").SetValue(strconv.Itoa(iLeft))
	cfg.Section("Window").Key("iTop").SetValue(strconv.Itoa(iTop))
	cfg.Section("Window").Key("isMaxWindow").SetValue("0")
	cfg.SaveTo(windowInfoFilePath)
}

func (a *AppFun) BeforeClose(ctx context.Context) (prevent bool) {
	if a.SysType == "windows" {
		a.IsShow = false
		// runtime.WindowHide(a.Ctx)
		// a.IsShow = false
	}

	if !a.UseBeforeClose { //不询问,直接关闭
		defer SaveWindowInfo(ctx)
		return false
	}

	go SaveWindowInfo(ctx) //趁机保存窗口位置和大小

	dialog, err := runtime.MessageDialog(ctx, runtime.MessageDialogOptions{
		Type:    runtime.QuestionDialog,
		Title:   "关闭确认",
		Message: "确定要退出程序吗？",
	})

	if err != nil {
		return false
	}

	return dialog != "Yes"
}

func (a *AppFun) HasWindow() string {
	return "local"
}

func (a *AppFun) CloseApp() string {
	go SaveWindowInfo(a.Ctx)

	if a.SysType == "windows" {
		a.IsShow = false
		runtime.WindowHide(a.Ctx)
		return ""
	}

	//a.IsShow = false
	runtime.Quit(a.Ctx) //请求关闭,如果有实现beforeClose旧会触发beforeClose

	//下面代码保留,这是没有实现beforeClose时的做法
	// dialog, err := runtime.MessageDialog(a.Ctx, runtime.MessageDialogOptions{
	// 	Type:    runtime.QuestionDialog,
	// 	Title:   "关闭确认",
	// 	Message: "确定要退出程序吗？",
	// })

	// if err != nil {
	// 	return "发生错误：" + err.Error()
	// }

	// if dialog == "Yes" {
	// 	//os.Exit(0)
	// 	runtime.Quit(a.Ctx)
	// 	return "确定关闭"
	// }

	// // a.beforeClose(a.Ctx)

	return "收到"
}

func (a *AppFun) CloseAppNoQuest() {
	go SaveWindowInfo(a.Ctx)
	a.UseBeforeClose = false
	runtime.Quit(a.Ctx) //请求关闭,如果有实现beforeClose旧会触发beforeClose
}

func (a *AppFun) SystemType() string {
	return a.SysType
}

// 隐藏窗口
func (a *AppFun) WindowHide() {
	go SaveWindowInfo(a.Ctx)
	runtime.WindowHide(a.Ctx)
	a.IsShow = false
}

// 显示窗口
func (a *AppFun) WindowShow() {
	runtime.WindowShow(a.Ctx)
	a.IsShow = true
}

// 执行JS函数
func (a *AppFun) WindowExecJS(jsCode string) {
	runtime.WindowExecJS(a.Ctx, jsCode)
}

// 窗口是否显示
func (a *AppFun) WindwIsShow() bool {
	return a.IsShow
}

// 设置窗口显示状态值
func (a *AppFun) SetWindwIsShow(bl bool) {
	a.IsShow = bl
}

// 使窗口全屏
func (a *AppFun) WindowFullscreen() {
	runtime.WindowFullscreen(a.Ctx)
}

// 最大化窗口以填满屏幕
func (a *AppFun) WindowMaximise() {
	runtime.WindowMaximise(a.Ctx)
}

// 窗口是否最大化
func (a *AppFun) WindowIsMaximised() bool {
	return runtime.WindowIsMaximised(a.Ctx)
}

// 在最大化和未最大化之间切换
func (a *AppFun) WindowToggleMaximise() {
	runtime.WindowToggleMaximise(a.Ctx)
}

// 窗口最小化
func (a *AppFun) WindowMinimise() {
	runtime.WindowMinimise(a.Ctx)
}

// 将窗口恢复到最小化之前的尺寸和位置
func (a *AppFun) WindowUnminimise() {
	runtime.WindowUnminimise(a.Ctx)
}

// 如果窗口最小化，则返回 true。
func (a *AppFun) WindowIsMinimised() bool {
	return runtime.WindowIsMinimised(a.Ctx)
}

// 设置窗口置顶或取消置顶
func (a *AppFun) WindowOnTop(top bool) {
	a.IsOnTop = top
	runtime.WindowSetAlwaysOnTop(a.Ctx, top)
}

// 设置窗口置顶或取消置顶
func (a *AppFun) ChangeWindowOnTop() bool {
	a.IsOnTop = !a.IsOnTop
	runtime.WindowSetAlwaysOnTop(a.Ctx, a.IsOnTop)
	return a.IsOnTop
}

// 设置窗口置顶或取消置顶
func (a *AppFun) IsWindowOnTop() bool {
	return a.IsOnTop
}

// 读取指定文件内容
func (a *AppFun) ReadFile(path string) (bool, string, interface{}) {
	if path == "" {
		return false, "路径参数缺失", 8001
	}

	//读取文件内容
	file, err := os.Open(path)
	if err != nil {
		return false, "打开文件异常:" + err.Error(), 8002
	}

	defer file.Close()

	//读取文件内容
	content, err := io.ReadAll(file)
	if err != nil {
		return false, "读取文件异常:" + err.Error(), 8003
	}

	return true, path, string(content)
}

// 保存文件内容
func (a *AppFun) SaveFile(path string, text string) (bool, string, interface{}) {
	if path == "" {
		return false, "路径参数缺失", 8001
	}

	file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return false, "打开文件异常:" + err.Error(), 8002
	}

	defer file.Close()
	_, err = file.WriteString(text)
	if err != nil {
		return false, "保存文件异常:" + err.Error(), 8003
	}

	return true, "保存文件成功", path
}

// 另存文件
func (a *AppFun) SaveAsFile(defaultPath string, text string) (bool, string, interface{}) {
	dialogOptions := runtime.SaveDialogOptions{
		DefaultDirectory: defaultPath,
		Title:            "选择保存位置",
		Filters: []runtime.FileFilter{{
			DisplayName: "Markdown",
			Pattern:     "*.md",
		}},
		CanCreateDirectories: true,
	}

	sFilePath, err := runtime.SaveFileDialog(a.Ctx, dialogOptions)
	if err != nil {
		return false, "打开文件异常:" + err.Error(), 8001
	}

	file, err := os.OpenFile(sFilePath, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return false, "打开文件异常:" + err.Error(), 8003
	}

	defer file.Close()
	_, err = file.WriteString(text)
	if err != nil {
		return false, "保存文件异常:" + err.Error(), 8004
	}

	return true, "保存文件成功", sFilePath
}

// 打开文件
func (a *AppFun) OpenFile(defaultPath string) (bool, string, interface{}) {
	dialogOptions := runtime.OpenDialogOptions{
		DefaultDirectory: defaultPath,
		Title:            "选择文件",
		Filters: []runtime.FileFilter{{
			DisplayName: "Markdown",
			Pattern:     "*.md",
		}, {
			DisplayName: "所有文件",
			Pattern:     "*.*",
		}},
	}

	sFilePath, err := runtime.OpenFileDialog(a.Ctx, dialogOptions)
	if err != nil {
		return false, "打开文件异常:" + err.Error(), 8001
	}

	if sFilePath == "" {
		return false, "文件路径为空", 8002
	}

	//读取文件内容
	file, err := os.Open(sFilePath)
	if err != nil {
		return false, "试图打开文件异常:" + err.Error(), 8003
	}

	defer file.Close()

	//读取文件内容
	content, err := io.ReadAll(file)
	if err != nil {
		return false, "读取文件异常:" + err.Error(), 8001
	}

	return true, sFilePath, string(content)
}

// Info消息
func (a *AppFun) InfoMsg(sTitle, sMessage string) {
	dialogOptions := runtime.MessageDialogOptions{
		Type:          runtime.InfoDialog,
		Title:         sTitle,
		Message:       sMessage,
		DefaultButton: "确定",
		//CancelButton:  string
	}

	runtime.MessageDialog(a.Ctx, dialogOptions)
}

// Warning消息
func (a *AppFun) WarningMsg(sTitle, sMessage string) {
	dialogOptions := runtime.MessageDialogOptions{
		Type:          runtime.WarningDialog,
		Title:         sTitle,
		Message:       sMessage,
		DefaultButton: "确定",
		//CancelButton:  string
	}

	runtime.MessageDialog(a.Ctx, dialogOptions)
}

// Error消息
func (a *AppFun) ErrorMsg(sTitle, sMessage string) {
	dialogOptions := runtime.MessageDialogOptions{
		Type:          runtime.ErrorDialog,
		Title:         sTitle,
		Message:       sMessage,
		DefaultButton: "确定",
		//CancelButton:  string
	}

	runtime.MessageDialog(a.Ctx, dialogOptions)
}

// Question消息
func (a *AppFun) QuestionMsg(sTitle, sMessage string) string {
	dialogOptions := runtime.MessageDialogOptions{
		Type:          runtime.QuestionDialog,
		Title:         sTitle,
		Message:       sMessage,
		DefaultButton: "确定",
		CancelButton:  "取消",
	}

	result, _ := runtime.MessageDialog(a.Ctx, dialogOptions)

	return result
}

// Question消息
func (a *AppFun) WindowPrint() string {
	runtime.WindowPrint(a.Ctx)

	return "响应"
}

// 打开选择目录对话框
func (a *AppFun) OpenDirectoryDialog(dialogOption string) (bool, string, interface{}) {
	var dialogOptions runtime.OpenDialogOptions
	if strings.TrimSpace(dialogOption) != "" {
		err := json.Unmarshal([]byte(dialogOption), &dialogOptions)
		if err != nil {
			return false, "选择文件发生异常", 1001
		}
	} else {
		dialogOptions = runtime.OpenDialogOptions{Title: "选择目录", DefaultDirectory: "C:\\"}
	}

	str, err := runtime.OpenDirectoryDialog(a.Ctx, dialogOptions)
	if err != nil {
		return false, "选择目录发生异常", 1002
	}

	return true, str, str
}

// 打开选择文件对话框
func (a *AppFun) OpenFileDialog(dialogOption string) (bool, string, interface{}) {
	var dialogOptions runtime.OpenDialogOptions
	if strings.TrimSpace(dialogOption) != "" {
		err := json.Unmarshal([]byte(dialogOption), &dialogOptions)
		if err != nil {
			return false, "选择文件发生异常", 1001
		}
	} else {
		dialogOptions = runtime.OpenDialogOptions{Title: "选择文件", DefaultDirectory: "C:\\"}
	}

	str, err := runtime.OpenFileDialog(a.Ctx, dialogOptions)
	if err != nil {
		return false, "选择文件发生异常", 1002
	}

	return true, str, str
}

// 打开保存文件对话框
func (a *AppFun) SaveFileDialog(dialogOption string) (bool, string, interface{}) {
	var dialogOptions runtime.SaveDialogOptions
	if strings.TrimSpace(dialogOption) != "" {
		err := json.Unmarshal([]byte(dialogOption), &dialogOptions)
		if err != nil {
			return false, "选择保存文件发生异常", 1001
		}
	} else {
		dialogOptions = runtime.SaveDialogOptions{Title: "选择保存文件", DefaultDirectory: "C:\\"}
	}

	str, err := runtime.SaveFileDialog(a.Ctx, dialogOptions)
	if err != nil {
		return false, "选择保存文件发生异常", 1002
	}

	return true, str, str
}

// 读取系统配置文件配置值
func (a *AppFun) ReadAppConfig(section, key string) map[string]interface{} {
	cfg, err := ini.Load(a.appConfigFile)
	if err != nil {
		return map[string]interface{}{"success": false, "msg": "读取配置文件发生异常:" + err.Error(), "data": 1001}
	}

	sectionTemp := cfg.Section(section)
	if sectionTemp == nil {
		return map[string]interface{}{"success": false, "msg": "指定分组不存在", "data": 1002}
	}

	keyTemp := sectionTemp.Key("iWidth")
	if keyTemp == nil {
		return map[string]interface{}{"success": false, "msg": "指定键不存在", "data": 1003}
	}

	return map[string]interface{}{"success": true, "msg": "读取配置文件成功", "data": keyTemp.String()}
}

// 打开外部程序,并传递参数
func (a *AppFun) OpenProgram(programPath, params string) map[string]interface{} {
	if programPath == "" {
		return map[string]interface{}{"success": false, "msg": "请求参数programPath缺失", "data": 1001}
	}

	// 获取程序的绝对路径
	programDir := filepath.Dir(programPath)

	cmd := exec.Command(programPath, params)

	// 设置工作目录
	cmd.Dir = programDir

	// 启动外部程序
	err := cmd.Start()
	if err != nil {
		return map[string]interface{}{"success": false, "msg": "请求打开引用程序" + programPath + "发生异常:" + err.Error(), "data": 1002}
	}

	return map[string]interface{}{"success": true, "msg": "请求已接收", "data": 8999}
}

/**
* 获取打印机名称
 */
func (a *AppFun) GetPrintName() map[string]interface{} {
	names, err := goprint.GetPrinterNames()
	if err != nil {
		return map[string]interface{}{"success": false, "msg": "查看打印机名称发生异常:" + err.Error(), "data": 1001}
	}

	return map[string]interface{}{"success": true, "msg": "获取打印机名称成功", "data": names}
}

/**
 * 打印机PDF文件
 * @param pdfPath 待打印文件路径
 * @param params 打印参数
 * @param printerDrive 打印机名称
 */
func (a *AppFun) PrintPdf(pdfPath, params, printerDrive string) map[string]interface{} {
	return PrintPdf(pdfPath, params, printerDrive)
}

/**
 * html转pdf
 * @param htmlInfo html信息
 * @return
 */
func (a *AppFun) HtmlToPdf(htmlInfo string) map[string]interface{} {
	return HtmlToPdf(htmlInfo)
}

/**
 * html打印
 * @param htmlInfo html信息
 * @param printerDrive 打印机名称
 * @return
 */
func (a *AppFun) HtmlPrint(htmlInfo, printerDrive string) map[string]interface{} {
	return HtmlPrint(htmlInfo, printerDrive)
}

/**
 * 发送信息
 * @param msg 信息
 */
func (a *AppFun) SendMsg(msg string) {
	SendUdp(msg)
}
